package fasdk

import (
	"fmt"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/client/channel"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/client/resmgmt"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/common/errors/retry"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/common/providers/fab"
	lcpackager "gitee.com/bupt-zkjc/fabric-sdk-go/pkg/fab/ccpackager/lifecycle"
	"gitee.com/bupt-zkjc/fabric-sdk-go/pkg/fabsdk"
	"gitee.com/bupt-zkjc/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl"
	pb "github.com/hyperledger/fabric-protos-go/peer"
	"path/filepath"
	"time"
)

//func (f *FabricClient)DeployCC(chaincodeName, version, language, policy, chaincodePath string, sequence int64,
//	peers []string, endorserPeers []string) (string, error) {
//	//安装chaincode
//	ccpkg, err := f.packageCC(chaincodeName, language, chaincodePath)
//	if err != nil {
//		logger.Error(err.Error())
//		return "Package chaincode failure", err
//	}
//	packageId, err := f.installChaincode(chaincodeName, ccpkg)
//	if err != nil {
//		logger.Error(err.Error())
//		return "Install chaincode failure", err
//	}
//	_, err = f.approveChaincode(chaincodeName, version, policy, packageId, sequence, endorserPeers)
//	if err != nil {
//		logger.Error(err.Error())
//		return "Approve chaincode failure", err
//	}
//	_, err = f.commitChaincode(chaincodeName, version, policy, sequence, endorserPeers)
//	if err != nil {
//		logger.Error(err.Error())
//		return "Commit chaincode failure", err
//	}
//	/*操作fabric end*/
//	return "Deploy chaincode success", nil
//}

func (f *FabricClient)PackageCC(chaincodeName, language, chaincodePath string) ([]byte, error) {
	var lanauageType pb.ChaincodeSpec_Type
	if language == "golang"{
		lanauageType = pb.ChaincodeSpec_GOLANG
	}else if language == "java"{
		lanauageType = pb.ChaincodeSpec_JAVA
	}else if language == "node" {
		lanauageType = pb.ChaincodeSpec_NODE
	}else if language == "car" {
		lanauageType = pb.ChaincodeSpec_CAR
	}else {
		lanauageType = pb.ChaincodeSpec_UNDEFINED
	}
	path := filepath.Join(f.GoPath, chaincodePath)
	fmt.Println(path)
	desc := &lcpackager.Descriptor{
		Path: path ,
		Type:  lanauageType,
		Label: chaincodeName,
	}
	ccPkg, err := lcpackager.NewCCPackage(desc)
	if err != nil {
		fmt.Print(err)
		return nil, err
	}
	return ccPkg, nil
}

func (f *FabricClient) InstallChaincode(chaincodeId string, ccPackage []byte) (string, error) {
	/*
		package chaincode para:
		- chaincodePath:  "github.com/fabcar/"
		- GoPath:         os.Getenv("GOPATH")
	*/
	installCCReq := resmgmt.LifecycleInstallCCRequest{
		Label:   chaincodeId,
		Package: ccPackage,
	}
	//packageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)
	var packageID string
	for _, client := range f.resmgmtClients {
		resp, err := client.LifecycleInstallCC(installCCReq, f.retry)
		if err != nil {
			logger.Error(err.Error())
			return "", err
		}
		for _, res := range resp{
			if res.Status == 200{
				packageID = res.PackageID
			}
		}

	}
	return packageID, nil
}
func (f *FabricClient) GetChaincodePackageId(chaincodeId string, ccPackage []byte) (string, error) {
	/*
		package chaincode para:
		- chaincodePath:  "github.com/fabcar/"
		- GoPath:         os.Getenv("GOPATH")
	*/
	installCCReq := resmgmt.LifecycleInstallCCRequest{
		Label:   chaincodeId,
		Package: ccPackage,
	}
	packageID := lcpackager.ComputePackageID(installCCReq.Label, installCCReq.Package)

	return packageID, nil
}
func (f *FabricClient) QueryInstalled(peers string) error {
	resp, err := f.resmgmtClients[0].LifecycleQueryInstalledCC(
		resmgmt.WithTargetEndpoints(peers),
		f.retry)
	if err != nil {
		fmt.Print(err)
		return err
	}
	fmt.Print(resp)
	return nil
}

func (f *FabricClient) ApproveChaincode(chaincodeId, version, policy, packageID string, sequence int64, peers []string) ([]byte, error) {
	//"OR ('Org1MSP.member','Org2MSP.member')"
	ccPolicy:= policydsl.SignedByAnyMember([]string{policy})
	approveCCReq := resmgmt.LifecycleApproveCCRequest{
		Name:               chaincodeId,
		Version:            version,
		PackageID:          packageID,
		Sequence:           sequence,
		EndorsementPlugin: "escc",
		ValidationPlugin:  "vscc",
		SignaturePolicy:    ccPolicy,
		InitRequired:       true,
	}
	//参与组织的节点
	var transactionId  fab.TransactionID
	for i, c := range f.resmgmtClients {
		txnID, err := c.LifecycleApproveCC(
			f.ChannelId,
			approveCCReq,
			resmgmt.WithTargetEndpoints(peers[i]),
			f.orderer,
			f.retry)
		if err != nil {
			logger.Error(err.Error())
			return nil, err
		}
		transactionId = txnID
	}
	return []byte(transactionId), nil
}

func (f *FabricClient) QueryApprovedChaincode(chaincodeId string, peers string) (interface{}, error) {
	queryApprovedCCReq := resmgmt.LifecycleQueryApprovedCCRequest{
		Name:     chaincodeId,
		Sequence: 1,
	}
	resp, err := f.resmgmtClients[0].LifecycleQueryApprovedCC(f.ChannelId, queryApprovedCCReq, resmgmt.WithTargetEndpoints(peers), f.retry)
	if err != nil {
		logger.Error(err)
		return nil, err
	}
	//logger.Info(resp)
	return resp, nil
}

func (f *FabricClient) CheckCCCommitReadiness(chaincodeId string, sequence int64, peers string) (map[string]bool, error) {
	ccPolicy := policydsl.SignedByAnyMember([]string{""})
	req := resmgmt.LifecycleCheckCCCommitReadinessRequest{
		Name:              chaincodeId,
		Version:           "1.0",
		EndorsementPlugin: "escc",
		ValidationPlugin:  "vscc",
		SignaturePolicy:   ccPolicy,
		Sequence:          sequence,
		InitRequired:      true,
	}
	resp, err := f.resmgmtClients[0].LifecycleCheckCCCommitReadiness(
		f.ChannelId,
		req,
		resmgmt.WithTargetEndpoints(peers),
		f.retry)
	if err != nil{
		logger.Error(err)
		return nil, err
	}
	logger.Info(resp)
	return resp.Approvals, nil
}

func (f *FabricClient) CommitChaincode(chaincodeId, version, policy string, sequence int64, peers []string) ([]byte, error) {
	//"OR ('Org1MSP.member','Org2MSP.member')"
	ccPolicy:= policydsl.SignedByAnyMember([]string{policy})
	commitCCReq := resmgmt.LifecycleCommitCCRequest{
		Name:               chaincodeId,
		Version:            version,
		Sequence:           sequence,
		EndorsementPlugin: "escc",
		ValidationPlugin:  "vscc",
		SignaturePolicy:    ccPolicy,
		InitRequired:       true,
	}
	//peersName := strings.Split(peers, ",")
	txnID, err := f.resmgmtClients[0].LifecycleCommitCC(
		f.ChannelId,
		commitCCReq,
		resmgmt.WithTargetEndpoints(peers...),
		f.orderer,
		f.retry)
	if err != nil {
		logger.Error(err.Error())
		return nil, err}
	return []byte(txnID), nil
}

func (f *FabricClient) QueryCommittedCC(chaincodeId string, peers []string) (resmgmt.LifecycleChaincodeDefinition, error) {
	req := resmgmt.LifecycleQueryCommittedCCRequest{
		Name: chaincodeId,
	}
	resp, err := f.resmgmtClients[0].LifecycleQueryCommittedCC(
		f.ChannelId,
		req,
		resmgmt.WithTargetEndpoints(peers...),
		f.orderer,
		f.retry)
	if err != nil{
		logger.Error(err)
		return resmgmt.LifecycleChaincodeDefinition{}, err
	}
	return resp[0], nil
}

func (f *FabricClient) InitChaincode(orgName string, peers []string, chaincodeId string, fcn string, args [][]byte) ([]byte, error) {
	client, err := channel.New(f.sdk.ChannelContext(f.ChannelId, fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(orgName)))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	reqPeers := channel.WithTargetEndpoints(peers...)
	resp, err := client.Execute(
		channel.Request{
			ChaincodeID: chaincodeId,
			Fcn: fcn,              //自定义？
			Args: args,
			IsInit: true,
		},reqPeers,
		channel.WithRetry(retry.DefaultChannelOpts))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	return []byte(resp.TransactionID), nil
}

func (f *FabricClient) GetChaincodeSequence(chaincodeName string, peers []string) (int64, error) {

	resp, err := f.QueryCommittedCC(chaincodeName, peers)
	if err != nil{
		logger.Error(err)
		return 0, err
	}

	return resp.Sequence, nil
}

func (f *FabricClient) QueryChaincode(chaincodeId, orgName string, fcn string, args [][]byte, peers []string) ([]byte, error) {

	client, err := channel.New(f.sdk.ChannelContext(f.ChannelId, fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(orgName)))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}

	resp, err := client.Query(channel.Request{
		ChaincodeID: chaincodeId,
		Fcn:         fcn,
		Args:        args,
	},
		channel.WithTargetEndpoints(peers...))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	logger.Info(string(resp.Payload))
	return resp.Payload, nil
}

func (f *FabricClient) InvokeChaincodeWithEvent(chaincodeId, fcn string, peers []string, args [][]byte, orgName string) ([]byte, error) {
	eventId := fmt.Sprintf("event%d", time.Now().UnixNano())

	client, err := channel.New(f.sdk.ChannelContext(f.ChannelId, fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(orgName)))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	// 注册事件
	reg, notifier, err := client.RegisterChaincodeEvent(chaincodeId, eventId)
	if err != nil {
		logger.Errorf("注册链码事件失败: %s", err)
		return nil, err
	}
	defer client.UnregisterChaincodeEvent(reg)

	req := channel.Request{
		ChaincodeID: chaincodeId,
		Fcn:         fcn,
		Args:        append(args, []byte(eventId)),
	}
	resp, err := client.Execute(req)
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}

	select {
	case ccEvent := <-notifier:
		logger.Infof("接收到链码事件: %v\n", ccEvent)
		return []byte(ccEvent.TxID), nil
	case <-time.After(time.Second * 30):
		logger.Info("不能根据指定的事件ID接收到相应的链码事件")
		return nil, fmt.Errorf("%s", "等到事件超时")
	}
	return []byte(resp.TransactionID), nil
}

func (f *FabricClient) InvokeChaincode(chaincodeId string, orgName, peers []string, fcn string, args [][]byte) ([]byte, error) {

	client, err := channel.New(f.sdk.ChannelContext(f.ChannelId, fabsdk.WithUser(f.OrgAdmin), fabsdk.WithOrg(orgName[0])))
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	req := channel.Request{
		ChaincodeID: chaincodeId,
		Fcn:         fcn,
		Args:        args,
	}
	//peersName := strings.Split(peers..., ",")
	reqPeers := channel.WithTargetEndpoints(peers...)
	resp, err := client.Execute(req, reqPeers)
	if err != nil {
		logger.Error(err.Error())
		return nil, err
	}
	return []byte(resp.TransactionID), nil
}
